home *** CD-ROM | disk | FTP | other *** search
- Path: solon.com!not-for-mail
- From: Chris Torek <torek@elf.bsdi.com>
- Newsgroups: comp.std.c,comp.lang.c.moderated
- Subject: Re: Integral promotion.
- Date: 14 Feb 1996 21:08:21 -0600
- Organization: Berkeley Software Design, Inc.
- Sender: clc@solutions.solon.com
- Approved: clc@solutions.solon.com
- Message-ID: <4fu835$9de@solutions.solon.com>
- References: <4fstj7$2l6@solutions.solon.com>
- Reply-To: torek@bsdi.com
- NNTP-Posting-Host: solutions.solon.com
-
- (I sent this as a reply by mail but am also posting it.)
-
- In article <4fstj7$2l6@solutions.solon.com> you write:
- >I got a problem understanding the rules on integral promotion in
- >the C language ...
-
- The rules in C are a little over-complicated (they were once entirely
- straightforward) but the complications mostly involve `unsigned'.
-
- It seems a little odd that you mention `Visual C++ 4.0', which
- sounds like a C++ compiler; C++ is an entirely different language
- with entirely different subtleties. (For instance, (sizeof 'x')
- is always 1 in C++ but is typically 2 or 4 in C.)
-
- >Consider the following function:
- >
- >short test(short x1, short x2);
-
- The prototype is important.
-
- We can mostly ignore the (valid) main() you provided...
-
- >short test(short x1, short x2)
- >{
- > short result;
- > result = x1 + x2; /* Warning: '=' : conversion from 'int '
- > to 'short ', possible loss of data */
- > return result;
- >}
-
- Again, we have a prototype-style definition, so the declaration
- and definition match.
-
- `Old' (pre-ANSI) C always performed `the integral promotions' on
- all parameters. For this reason, ANSI/ISO C does the same with
- parameters for which a prototype is not available -- this covers
- both `functions without prototypes' and `functions whose prototype
- ends in ", ..."', e.g., printf(). (This means that, for instance,
- you can never pass a short to printf(). By the time printf()
- receives it, the value has been converted to an int.) In Old C,
- if you defined a function like this:
-
- void func(i, s, c)
- int i;
- short s;
- char c;
- {
- }
-
- the compiler would take the three `int's that it knew had been
- passed (due to the integral promotions), put the first in `i',
- narrow the second to `short' and put it in `s', and narrow the
- third and put it in `c'. This promotion and narrowing sometimes
- needs extra instructions (it depends on the machine, and maybe
- the type as well).
-
- For efficiency and/or less-surprising behavior, ANSI C says that
- prototyped parameters (as you have here) are not promoted. (Many
- compilers simply promote them at the caller, and then narrow them
- before entering the function, just as for Old C, but they can now
- avoid these steps in this case.)
-
- Because ANSI C requires promotion for non-prototypes and non-promotion
- for prototypes, it is important to make sure you never mix up the
- two kinds of declarations and definitions when you use `narrow'
- types (char, short, signed char, unsigned char, unsigned short,
- and float).
-
- Anyway, with all that out of the way, we have a correct declaration
- and definition for function test(), with `x1', `x2', and `result' all
- being `short'.
-
- Now, the last hard part is the line:
-
- result = x1 + x2;
-
- This takes the values in x1 and x2 (both of type (signed) short
- and hence in the range [SHRT_MIN .. SHRT_MAX]), performs the
- integral promotions (yeilding values of type (signed) int but still
- in the range [SHRT_MIN .. SHRT_MAX]), and adds them. At this
- point, the sum still has type (signed) int and could potentially
- be in the range [2*SHRT_MIN .. 2*SHRT_MAX].
-
- Now, if `int' and `short' are the same width, a result that would
- be outside the range [SHRT_MIN .. SHRT_MAX] is also outside the
- range [INT_MIN .. INT_MAX], and the behavior is undefined (ANSI C
- section 3.3, p. 39, ll. 15--17).
-
- On the other hand, if `int's are wider than `short's, we are pretty
- safe (maybe not completely safe, but pretty safe anyway) in assuming
- that INT_MIN <= 2*SHRT_MIN and INT_MAX >= 2*SHRT_MAX. Most likely
- INT_MIN and INT_MAX are something like 65536 times bigger. Either
- way, the sum is not outside the representable range and the undefined
- clause in 3.3 does not apply.
-
- The final step is to convert the sum back to type `short'. Here
- things get a little fuzzy; comp.std.c has had arguments as to what
- happens if the sum is `int', `int' is wider than `short', and the
- sum is not representable as a `short'. The Standard distinguishes
- between `implicit' and `explicit' conversions, and this is an
- `implicit' conversion (casts are the usual, perhaps the only, way
- to write explicit conversions). Some have argued that an explicit
- conversion should never cause an overflow, but there seems to be
- no wording in the standard to justify this position. There appears
- to be agreement that implicit conversion might overflow. The
- behavior on overflow would be undefined.
-
- Your particular compiler is likely to ignore the overflow and
- truncate the result (most compilers do, and the warning -- `possible
- loss of data' -- suggests that yours does).
-
- It is interesting, however, to note that if overflow will ever occur,
- it will occur no matter whether the ranges of short and int coincide
- or not. The only thing that changes here is the exact operation that
- overflows: the sum, or the implicit conversion in the assignment.
-
- The compiler's warning suggests (but by no means guarantees) that
- this particular compiler has wider `int's than `short's. Since this
- is comp.lang.c.moderated (as well as comp.std.c), it is reasonable
- to ask whether the warning the compiler emits is `reasonable'. I
- think not, but this is purely my opinion. The Standard itself allows
- arbitrary warnings, so it is no help here.
-
- >my compiler (Microsoft Visual C++ 4.0), automagically converts
- >my short-parameters to int's, even though all variables involved
- >are short. I know that the standard says that all arguments can
- >be converted to the biggest 'type' of all the arguments,
-
- See the detailed explanation above :-)
-
- >but is it correct that char's and short's always are promoted to int's,
- >without regard to the other arguments in the expression?
-
- In expressions, yes.
-
- >The reason I ask is that I am writing code that should be
- >portable from a 16-bits environment (where short and int are the
- >same size) to a 32-bits environment. (My 16-bits compiler does
- >not complain about the code).
-
- Perhaps it should, since the sum itself can overflow here just as
- easily as a conversion from a 32-bit-int sum to a 16-bit short
- `result'. Again, the Standard is no help here, and my personal
- opinion is that a warning is unwarranted (because such a warning
- should then be given for virtually every operator).
-
- I would find an `assignment may truncate' warning appropriate, and
- maybe even helpful, when the target of a complex expression has a
- narrower type than at least one of the operands *before* promotion.
- That is, if someone writes:
-
- short a, b, prod;
-
- prod = a * b;
-
- I would object (mildly) to a warning, but if they had:
-
- int a;
- short b, prod;
-
- prod = a * b;
-
- I would not. In this case, I think an explicit truncation somewhere
- should eliminate the warning, so that either:
-
- prod = (short)(a * b);
-
- or:
-
- prod = (short)a * b; /* not the same thing at all! */
-
- should not warn. But, again, this is simply my opinion.
- --
- In-Real-Life: Chris Torek, Berkeley Software Design Inc
- El Cerrito, CA Domain: torek@bsdi.com +1 510 234 3167
-